using TMPro;
using UnityEngine;

/// <summary>
/// Game 类是整个游戏的主控制器，负责协调 Ball（球）、Paddle（挡板）、Camera（相机）等组件的行为
/// </summary>
public class Game : MonoBehaviour
{
    [SerializeField]
    Ball ball;                                      // 游戏中的球对象，控制其运动与碰撞

    [SerializeField]
    Paddle bottomPaddle, topPaddle;                 // 两个挡板，分别位于场景下方和上方

    [SerializeField, Min(0f)]
    Vector2 arenaExtents = new Vector2(10f, 10f);   // 游戏竞技场的边界范围，x 和 y 分别代表横向与纵向的最大范围

    [SerializeField, Min(2)]
    int pointsToWin = 3;                            // 获胜所需的得分点数，例如先得 3 分的一方获胜

    [SerializeField]
    TextMeshPro countdownText;                      // 倒计时显示文本（TMP 文本组件），用于显示“游戏结束”或重启倒计时

    [SerializeField, Min(1f)]
    float newGameDelay = 3f;                        // 每局游戏结束后，开始新一轮前的等待时间

    [SerializeField]
    LivelyCamera livelyCamera;                      // 自定义相机控制器，用于在球碰撞边界时添加动态镜头效果

    float countdownUntilNewGame;                    // 剩余的新一轮开始倒计时时间


    void Awake () 
    {
        // 初始化倒计时时间，设置为预设的 newGameDelay
        countdownUntilNewGame = newGameDelay;
    }

    // 开始一场新游戏，重置球和两个挡板的状态
    void StartNewGame ()
    {
        ball.StartNewGame();                          // 球开始新的一轮，设置初始位置与速度
        bottomPaddle.StartNewGame();                  // 下方挡板重置得分与 AI 行为
        topPaddle.StartNewGame();                     // 上方挡板重置得分与 AI 行为
    }
    
    void Update ()
    {
        // 让两个挡板根据球的当前 X 位置进行移动，限制在竞技场 X 轴范围内
        bottomPaddle.Move(ball.Position.x, arenaExtents.x);
        topPaddle.Move(ball.Position.x, arenaExtents.x);

        if (countdownUntilNewGame <= 0f)
        {
            // 如果倒计时已结束，执行正常的游戏逻辑更新
            UpdateGame();
        }
        else
        {
            // 如果仍在倒计时阶段，更新倒计时显示
            UpdateCountdown();
        }
    }

    /// <summary>
    /// 游戏逻辑更新：处理球的运动、碰撞检测与响应、视觉更新等
    /// </summary>
    void UpdateGame ()
    {
        ball.Move();                                  // 球按照当前速度移动

        BounceYIfNeeded();                            // 检查是否需要 Y 轴方向（上下）的碰撞反弹
        BounceXIfNeeded(ball.Position.x);             // 检查是否需要 X 轴方向（左右）的碰撞反弹

        ball.UpdateVisualization();                   // 更新球及其相关粒子系统在场景中的位置
    }

    /// <summary>
    /// 倒计时逻辑更新：处理游戏结束后的等待与重启提示
    /// </summary>
    void UpdateCountdown ()
    {
        countdownUntilNewGame -= Time.deltaTime;      // 倒计时时间递减

        if (countdownUntilNewGame <= 0f)
        {
            // 倒计时结束，隐藏倒计时文本并开始新游戏
            countdownText.gameObject.SetActive(false);
            StartNewGame();
        }
        else
        {
            // 倒计时未结束，显示剩余时间（向上取整）
            float displayValue = Mathf.Ceil(countdownUntilNewGame);
            if (displayValue < newGameDelay)
            {
                // 仅当剩余时间小于总延迟时才显示，避免一开始就显示完整倒计时
                countdownText.SetText("{0}", displayValue);
            }
        }
    }

    /// <summary>
    /// 检查球是否超出 X 轴边界（左右边界），如果超出则触发反弹与相机效果
    /// </summary>
    /// <param name="x"></param>
    void BounceXIfNeeded (float x)
    {
        float xExtents = arenaExtents.x - ball.Extents;  // 计算球允许移动的最大 X 范围

        if (x < -xExtents)
        {
            // 球超出左边界，触发相机震动效果，并让球从左边反弹
            livelyCamera.PushXZ(ball.Velocity);
            ball.BounceX(-xExtents);
        }
        else if (x > xExtents)
        {
            // 球超出右边界，触发相机震动效果，并让球从右边反弹
            livelyCamera.PushXZ(ball.Velocity);
            ball.BounceX(xExtents);
        }
    }

    /// <summary>
    /// 检查球是否超出 Y 轴边界（上下边界），如果超出则触发反弹逻辑
    /// </summary>
    void BounceYIfNeeded ()
    {
        float yExtents = arenaExtents.y - ball.Extents;  // 计算球允许移动的最大 Y 范围

        if (ball.Position.y < -yExtents)
        {
            // 球超出下边界，与下挡板（bottomPaddle）和上挡板（topPaddle）进行交互
            BounceY(-yExtents, bottomPaddle, topPaddle);
        }
        else if (ball.Position.y > yExtents)
        {
            // 球超出上边界，与上挡板和下挡板进行交互
            BounceY(yExtents, topPaddle, bottomPaddle);
        }
    }
    
    /// <summary>
    /// 处理球在 Y 轴方向上与某个边界的碰撞反弹逻辑
    /// </summary>
    /// <param name="boundary">碰撞的边界位置（比如下边界或上边界）</param>
    /// <param name="defender">处于准备防守的挡板</param>
    /// <param name="attacker">发起进攻的挡板</param>
    void BounceY (float boundary, Paddle defender, Paddle attacker)
    {
        // 计算球从当前位置到边界还需要的时间
        float durationAfterBounce = (ball.Position.y - boundary) / ball.Velocity.y;

        // 估算球撞击边界时，它的 X 坐标位置
        float bounceX = ball.Position.x - ball.Velocity.x * durationAfterBounce;

        // 先检查 X 轴是否也需要反弹（比如球撞到边墙）
        BounceXIfNeeded(bounceX);

        // 再次计算 bounceX（冗余，可以优化），然后触发 Y 轴反弹的相机效果
        bounceX = ball.Position.x - ball.Velocity.x * durationAfterBounce;
        livelyCamera.PushXZ(ball.Velocity);

        // 球在 Y 轴方向上真正发生反弹
        ball.BounceY(boundary);

        // 检查防守方挡板（defender）是否成功击中球
        if (defender.HitBall(bounceX, ball.Extents, out float hitFactor))
        {
            // 如果击中，则根据击中位置调整球的新 X 位置与速度
            ball.SetXPositionAndSpeed(bounceX, hitFactor, durationAfterBounce);
        }
        else
        {
            // 如果未击中，触发相机垂直震动效果，并检查攻击方（attacker）是否因此得分
            livelyCamera.JostleY();

            if (attacker.ScorePoint(pointsToWin))
            {
                // 如果攻击方得分达到胜利条件，则结束游戏
                EndGame();
            }
        }
    }

    // 游戏结束处理：显示游戏结束提示，启动新一轮倒计时
    void EndGame ()
    {
        countdownUntilNewGame = newGameDelay;         // 重置倒计时时间
        countdownText.SetText("GAME OVER");           // 设置倒计时文本为“GAME OVER”
        countdownText.gameObject.SetActive(true);     // 显示倒计时文本
        ball.EndGame();                               // 隐藏球对象，停止其运动与特效
    }
}